/**
 * com.chaeron.GPS - An API for access to GPS devices.
 *
 *
 * Copyright (c) 2001, 2002 Chaeron Corporation,
 * All rights reserved.
 * 
 * Chaeron grants you a non-exclusive license to use, modify and re-distribute
 * this program under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version, provided that this copyright notice and 
 * license appear on all copies of the software.
 * 
 * Software is provided "AS IS," without a warranty of any kind. ALL EXPRESS OR
 * IMPLIED REPRESENTATIONS AND WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE
 * HEREBY EXCLUDED. THE ENTIRE RISK ARISING OUT OF USING THE SOFTWARE IS ASSUMED
 * BY THE LICENSEE. See the GNU General Public License for more details.
 *
 * CHAERON AND ITS LICENSORS SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY
 * LICENSEE OR ANY THIRD PARTY AS A RESULT OF USING OR DISTRIBUTING SOFTWARE.
 * IN NO EVENT WILL CHAERON OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE,
 * PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL
 * OR PUNITIVE DAMAGES, HOWEVER CAUSED AN REGARDLESS OF THE THEORY OF LIABILITY,
 * ARISING OUT OF THE USE OF OR INABILITY TO USE SOFTWARE, EVEN IF CHAERON HAS
 * BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

 * @version     1.02
 * @author      Andrzej Jan Taramina, Chaeron Corporation
 *              andrzej@chaeron.com
 */

package com.chaeron.GPS.event.decorator;

//***************************************************************************
//*
//*     Imports
//*
//***************************************************************************/

import com.chaeron.GPS.*;
import com.chaeron.GPS.event.*;

import com.chaeron.platform.common.*;


//***************************************************************************
//*
//*    GPSEventDecorator class definition
//*
//***************************************************************************/

/**
 * The GPSEventDecorator decorator is the base class for extended functions 
 * on top of GPSEvent interfaces.
 * These include various output functions for string and XML output.
 *
 */

public abstract class GPSEventDecorator implements GPSValue
{
	
	//***************************************************************************
	//*
	//*     Constant definitions
	//*
	//***************************************************************************/
	
	private final static char[] HEX_CHARS = {	'0','1','2','3','4','5','6','7','8','9', 'A','B','C','D','E','F'};
	
	//***************************************************************************
	//*
	//*     Static fields
	//*
	//***************************************************************************/
	
	private	static StringBuffer 	buffer				= new StringBuffer( 4096 );
	
	
	//***************************************************************************
	//*
	//*     Attributes
	//*
	//***************************************************************************/
	
	private int				methodsImplemented		= 0;
	
	private	StringBuffer	outString				= null;
	private Catalog			outCatalog				= null;
	
	
	//***************************************************************************
	//*
	//*     Constructor
	//*
	//***************************************************************************/
	
	/**
	 * Create a GPSEventDecorator
	 *
	 */
	
	public GPSEventDecorator()
	{
		if( Compile.INCLUDE_EVENT_TOXML ) {
			methodsImplemented |= GPSValue.GPSVALUE_METHOD_TOXML;
		}
	}
	
	//***************************************************************************
	//*
	//*     Accessor Methods
	//*
	//***************************************************************************/
	
	/**
	 * Get the Methods Implemented flags
	 *
	 * @return		methods implemented flags
	 */
	
	public int getMethodsImplemented() 
	{
		return( methodsImplemented );
	}
	
	/**
	 * Set the methodsImplemented flags
	 *
	 * @param	flags	field flags
	 */
	
	public void setMethodsImplemented( int flags ) 
	{
		methodsImplemented = flags;
	}
	
	/**
	 * Add to the methodsImplemented flags
	 *
	 * @param	flags	field flags
	 */
	
	public void addMethodsImplemented( int flags ) 
	{
		methodsImplemented = methodsImplemented | flags;
	}
	
	/**
	 * Test if a method is implemented
	 *
	 * @param	methodFlag	method to test
	 */
	
	public boolean isMethodImplemented( int methodFlag )
	{
		return( ( methodsImplemented & methodFlag ) != 0 );
	}
	
	/**
	 * Get the Methods Implemented flags
	 *
	 * @return		methods implemented flags
	 */
	
	protected Catalog getOutCatalog() 
	{
		Catalog catalog = null;
		
		if( Compile.INCLUDE_EVENT_SAVETOCATALOG ) {
			catalog = outCatalog;
		}
		
		return( catalog );
	}
	
	//***************************************************************************
	//*
	//*     XML Formatted Output Methods
	//*
	//***************************************************************************/
	
	/**
	 * Return this Event Value as XML, XML declaration is included
	 *
	 * @return Event Value as XML
	 */
	
	public String toXML()
	{
		return( toXML( true, true ) );
	}
	
	/**
	 * Return this Event Value as XML
	 *
	 * @param declaration include XML Declaration at start?
	 * @param namespace include XML namespace at start?
	 * @return Event Value as XML
	 */
	
	public String toXML( boolean declaration, boolean namespace )
	{
		String		value	= null;
		
		if( Compile.INCLUDE_EVENT_TOXML ) {
			outString = new StringBuffer();
			outCatalog = null;
			
			if( declaration ) {
				writeString( GPSXMLTag.XML_DECLARATION );
			}
			
			toXMLImpl( namespace );
			
			value = outString.toString();
		}
		
		return( value );
	}
	
	/**
	 * Write this Event Value as XML to a catalog, XML declaration is included
	 *
	 * @param declaration include XML Declaration at start?
	 */
	
	public void toXML( Catalog catalog ) 
	{
		toXML( catalog, true, true );
	}
	

	/**
	 * Write this Event Value as XML to a catalog
	 *
	 * @param catalog	catalog to write to.
	 * @param declaration include XML Declaration at start?
	 * @param namespace include XML namespace at start?
	 */
	
	public void toXML( Catalog catalog, boolean declaration, boolean namespace ) 
	{
		if( Compile.INCLUDE_EVENT_TOXML ) {
			outString = null;
			outCatalog = catalog;
			
			if( declaration ) {
				writeString( GPSXMLTag.XML_DECLARATION );
			}
			
			toXMLImpl( namespace );
		}
	}
	
	
	/**
	 * Write this Event Value as XML.  This method is used internally by composite Events
	 * such as Routes/Tracklogs to output their child events (Waypoints, etc.)
	 *
	 * @param catalog	catalog to write to.  Null if returning value as a string.
	 * @param declaration include XML Declaration at start?
	 * @param namespace include XML namespace at start?
	 * @return	XML as string, if not writing to catalog. Returns empty string if writing to catalog.
	 */
	
	protected String childToXML( Catalog catalog ) 
	{
		String		xml	= "";
		
		if( Compile.INCLUDE_EVENT_TOXML ) {
			if( catalog != null ) {
				toXML( catalog, false, false );
			} else {
				xml = toXML( false, false );
			}
		}
		
		return( xml );
	}
	
	
	//***************************************************************************
	//*
	//*     XML Helper Methods
	//*
	//***************************************************************************/
	
	/**
	 * toXMLImpl subclass methods should use this method to write their output!
	 *
	 * @param string	string to be written
	 */
	
	protected void writeString( String line )
	{
		if( outString != null ) {
			outString.append( line );
		}
		
		if( Compile.INCLUDE_EVENT_SAVETOCATALOG ) {
			if( outCatalog != null ) {
				int		recordPos = outCatalog.addRecord( line.length() );
	
				if( recordPos != -1 ) {
					outCatalog.writeBytes( line.getBytes(), 0, line.length() );
					outCatalog.setRecordPos( -1 );
				}
			}
		}
	}
	
	/**
	 * toXMLImpl subclass methods should use this method to write their output!
	 *
	 * @param catalog	catalog to write to.
	 */
	
	public static void writeString( Catalog catalog, String line )
	{
		if( Compile.INCLUDE_EVENT_SAVETOCATALOG ) {
			int		recordPos = catalog.addRecord( line.length() );
			
			if( recordPos != -1 ) {
				catalog.writeBytes( line.getBytes(), 0, line.length() );
				catalog.setRecordPos( -1 );
			}
		}
	}
	
	/**
	 * Generate an XML Start tag
	 *
	 * @param tagname		the tag name
	 * @param namespace		imclude namespace specifier in this tag?
	 * @return				the XML Start tag
	 */
	
	public static String genXMLStartTag( String tagName, boolean namespace ) 
	{
		return( genXMLTag( tagName, null, null, true, false, namespace ) );
	}
	
	/**
	 * Generate an XML End tag
	 *
	 * @param tagname		the tag name
	 * @return				the XML End tag
	 */
	
	public static  String genXMLEndTag( String tagName ) 
	{
		return( genXMLTag( tagName, null, null, false, true, false ) );
	}
	
	/**
	 * Generate an XML Value tag (includes start and end tags)
	 *
	 * @param tagname		the tag name
	 * @param value			the value of the tag
	 * @return				the XML Value tag
	 */
	
	public static String genXMLValueTag( String tagName, String value ) 
	{
		return( genXMLTag( tagName, null, value, true, true, false ) );
	}
	
	/**
	 * Generate an XML Value tag (includes start and end tags)
	 *
	 * @param tagname		the tag name
	 * @param attribute		the attribute(s) of the tag
	 * @param value			the value of the tag
	 * @return				the XML Value tag
	 */
	
	public static String genXMLValueTag( String tagName, String attributes, String value ) 
	{
		return( genXMLTag( tagName, attributes, value, true, true, false ) );
	}
	
	/**
	 * Generate an XML Value tag (includes start and end tags)
	 *
	 * @param tagname		the tag name
	 * @param attributes	the attribute(s) of the tag
	 * @param value			the value of the tag
	 * @param start			include a start tag?
	 * @param end			include an end tag?
	 * @param namespace		include a namespace specifier for this tag?
	 */
	
	public static String genXMLTag( String tagName, String attributes, String value, boolean start, boolean end, boolean namespace )
	{
		buffer.delete( 0, buffer.length() );
		
		if( start ) {
			buffer.append( "<" );
			buffer.append( tagName );
			if( namespace ) {
				buffer.append( " xmlns=\"" );
				buffer.append( GPSXMLTag.XML_NAMESPACE );
				buffer.append( "\"" );
			}
			if( attributes != null && attributes.length() > 0 ) {
				buffer.append( " " );
				buffer.append( attributes );
			}
			buffer.append( ">" );
		}
		
		if( value != null && value.length() > 0 ) {
			buffer.append( value );
		}
		
		if( end ) {
			buffer.append( "</" );
			buffer.append( tagName );
			buffer.append( ">" );
		}
		
		buffer.append( '\n' );
		
		return( buffer.toString() );
	}
	
	
	/**
	 * Generate an XML Attribute
	 *
	 * @param attribute		the attribute
	 * @param value			the value of the attribute
	 * @return				the XML Attribute
	 */
	
	public static String genXMLAttribute( String attribute, String value ) 
	{
		buffer.delete( 0, buffer.length() );
		
		buffer.append( attribute );
		buffer.append( "=\"" );
		buffer.append( value );
		buffer.append( "\"" );
		
		return( buffer.toString() );
	}
	
	/**
	 * Generate a Hex string for a byte array
	 *
	 * @param bytes			the byte array to be converted to hex
	 * @return				the Hex string
	 */
	
	protected String toHexString( byte[] bytes )
	{
		char[] 			hex 	= new char[ 2 ];
		int				i;
		
		buffer.delete( 0, buffer.length() );
		
		for( i = 0; i < bytes.length; i++ ) {
			hex[ 0 ] = HEX_CHARS[ ( ( bytes[i] + 256 ) & 0xf0 ) >> 4 ];
			hex[ 1 ] = HEX_CHARS[ ( bytes[i] + 256 ) & 0x0f ];
			
			buffer.append( hex[ 0 ] );
			buffer.append( hex[ 1 ] );
		}
		
		return( buffer.toString() );
	}
	
	//***************************************************************************
	//*
	//*     Abstract Output Methods (implemented by subclasses
	//*
	//***************************************************************************/
	
	/**
	 * This method should write XML output using writeString 
	 *
	 * @param namespace include XML namespace at start?
	 * @see writeString()
	 */
	
	abstract void toXMLImpl( boolean namespace );
}
